//
// Created by Immortal on 4/28/2021.
//

#include "Racoon.h"

Racoon::Racoon(uint16_t uPort, const char* pAccessDir)
{
    //服务器
    m_pServer = new Server(uPort);
    //处理函数
    m_pmapHandlingFunc = new std::map<std::string, HANDLING_FUNC>;
    //内容类型
    m_pContentType = new std::map<std::string, const char*>;
    //访问目录
    m_pAccessDir = pAccessDir;
    //初始化内容类型
    InitContentType();

    //设置默认处理函数
    m_pServer->SetHandlingFunc((Server::PHANDLINGFUNC)Racoon::DefaultHandling, this);
}

Racoon::~Racoon()
{
    if(m_pServer != NULL)
    {
        delete m_pServer;
        m_pServer = NULL;
    }
    if(m_pContentType != NULL)
    {
        delete m_pContentType;
        m_pContentType = NULL;
    }
}

//初始化内容类型
void Racoon::InitContentType()
{
    (*m_pContentType)["HTML"] = CT_TEXT_HTML;
    (*m_pContentType)["TXT"] = CT_TEXT_PLAIN;
    (*m_pContentType)["XML"] = CT_TEXT_XML;
    (*m_pContentType)["GIF"] = CT_IMAGE_GIF;
    (*m_pContentType)["PNG"] = CT_IMAGE_PNG;
    (*m_pContentType)["JPG"] = CT_IMAGE_JPEG;

    //(*m_pContentType)["XML"] = CT_APP_XML;
    (*m_pContentType)["JSON"] = CT_APP_JSON;
    (*m_pContentType)["PDF"] = CT_APP_PDF;
}

//设置请求头最大限制
void Racoon::SetReqHeadLimit(int nSize)
{
    m_pServer->SetReqHeadLimit(nSize);
}

//设置请求内容最大限制
void Racoon::SetReqContentLimit(int nSize)
{
    m_pServer->SetReqContentLimit(nSize);
}

//获取内容类型
const char* Racoon::GetContentType(const char* key)
{
    auto it = m_pContentType->find(key);
    if(it == m_pContentType->end())
    {
        //返回默认 - 二进制流
        return CT_APP_OCTET_STREAM;
    }

    return it->second;
}

//设置处理函数
void Racoon::SetHandlingFunc(const char* type, const char* path, PHANDLINGFUNC func, void* pArg)
{
    std::string strKey = type;

    //转换大写
    std::transform(strKey.begin(), strKey.end(), strKey.begin(), ::toupper);

    //路径
    strKey += path;

    //构造函数结构
    HANDLING_FUNC hf;
    hf.pFun = func;
    hf.pArg = pArg;

    //添加
    (*m_pmapHandlingFunc)[strKey] = hf;

}

//获取处理函数
Racoon::PHANDLINGFUNC Racoon::GetHandlingFunc(const char* name)
{
    auto it = m_pmapHandlingFunc->find(name);
    if(it == m_pmapHandlingFunc->end())
    {
        //返回默认 - 二进制流
        return nullptr;
    }

    return it->second.pFun;
}

//执行
void Racoon::Exec()
{
    m_pServer->Exec();
}

//默认处理
void Racoon::DefaultHandling(Http& http, Racoon* pRacoon)
{
    //获取请求类型字符串
    const char* pReqType = http.GetReqTypeStr();
    //获取请求路径
    const char* pReqPath = http.GetReqPath();

    //请求函数对应键
    std::string strReqKeyOfFunc = pReqType;
    strReqKeyOfFunc += pReqPath;

    //查找是否有对应的处理函数
    auto itFunc = pRacoon->m_pmapHandlingFunc->find(strReqKeyOfFunc);
    if(itFunc != pRacoon->m_pmapHandlingFunc->end())
    {
        //执行处理函数
        itFunc->second.pFun(http, itFunc->second.pArg);
        return;
    }

    //没有对应的处理函数 - 执行默认
    //获取请求类型
    Http::REQ_TYPE rt = http.GetReqType();
    switch(rt)
    {
        case Http::REQ_NULL:
            break;
        case Http::REQ_GET:
            DefaultGet(http, pRacoon);
            break;
        case Http::REQ_POST:
            DefaultPost(http, pRacoon);
            break;
    }
}

//GET
void Racoon::DefaultGet(Http& http, Racoon* pRacoon)
{
    //获取访问目录
    std::string strReqPath = pRacoon->m_pAccessDir;
    strReqPath += http.GetReqPath();
    //获取后缀
    const char* pSuffix = http.GetReqSuffix();
    //获取对应的响应字段值
    const char* pContentType = pRacoon->GetContentType(pSuffix);
    //设置响应类型
    http.SetContentType(pContentType);

    //不支持断点
    http.SetResField("Accept-Ranges", "none");

    //设置响应文件
    http.SetResFile(strReqPath.c_str());
    //获取响应文件大小
    if(http.GetResFileSize() == -1)
    {
        //找不到文件
        http.SetResStatusCode(sc404);
        http.SetContentType(CT_TEXT_HTML);
        const char* p404 = "<h1>404 Not Found</h1>";
        http.SetContent(p404, strlen(p404));
    }
}

//POST
void Racoon::DefaultPost(Http& http, Racoon* pRacoon)
{
    DefaultGet(http, pRacoon);
}
